package com.zxb.base.watch.util;

import com.alibaba.fastjson.JSONObject;
import com.zxb.base.watch.service.WatchConditionService;
import com.zxb.base.watch.vo.WatchVo;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.file.*;
import java.util.List;

import com.zxb.core.socket.WebSocketServer;

/**
 * @author Zxb
 * 监控工具类
 */
@Component
@AllArgsConstructor
@Slf4j
public class WatchUtil {
    private final WatchConditionService watchConditionService;

    public void startWatch(WatchVo watchVo) {
        String watchCode = watchVo.getCode();
        Path fullPath = Paths.get(watchVo.getFilePath());
        WatchService watchService;
        //如果监控对象不存在，则进行初始化
        if (!WatchMap.contains(watchCode)) {
            Path folderToWatch = Paths.get(watchVo.getFolderPath());
            // 创建WatchService
            try {
                watchService = FileSystems.getDefault().newWatchService();
                // 注册对目录的ENTRY_MODIFY事件的监听
                folderToWatch.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
            } catch (IOException e) {
                String msg = "WatchMsg:注册监控服务时出现异常";
                WebSocketServer.sendMsg(watchVo.getClientId(), msg);
                e.printStackTrace();
                return;
            }
            WatchMap.put(watchCode, watchVo, watchService);
            String msg = "WatchMsg:开始监控文件【" + watchVo.getFilePath() + "】";
            WebSocketServer.sendMsg(watchVo.getClientId(), msg);
        } else {
            watchService = WatchMap.getService(watchCode);
            String msg = "WatchMsg:继续监控文件【" + watchVo.getFilePath() + "】";
            WebSocketServer.sendMsg(watchVo.getClientId(), msg);
        }
        printLastLines(watchVo.getClientId(), fullPath, watchVo.getLinesToOutput());
        // 根据条件进行监控循环，等待文件变化事件
        while (watchConditionService.continueWatch(watchVo)) {
            WatchKey key;
            try {
                key = watchService.take();
            } catch (Exception ex) {
                return;
            }

            for (WatchEvent<?> event : key.pollEvents()) {
                WatchEvent.Kind<?> kind = event.kind();
                //目录更改事件
                if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
                    //符合监控的文件
                    if (fullPath.endsWith(watchVo.getFileName())) {
                        printLastLines(watchVo.getClientId(), fullPath, watchVo.getLinesToOutput());
                    }
                }
            }
            // 重置key，以便接收新的文件变化事件
            boolean valid = key.reset();
            if (!valid) {
                break;
            }
        }
        String msg = "WatchMsg:暂停监控";
        WebSocketServer.sendMsg(watchVo.getClientId(), msg);
    }

    public void endWatch(WatchVo watchVo) {
        String watchCode = watchVo.getCode();
        log.info("停止监控：{}", watchCode);
        WatchService watchService = WatchMap.getService(watchCode);
        String msg;
        if (watchService != null) {
            try {
                watchService.close();
                msg = "WatchMsg:成功停止监控";
            } catch (IOException e) {
                e.printStackTrace();
                msg = "WatchMsg:停止监控时发生异常";
            }
        } else {
            msg = "WatchMsg:已经停止监控";
        }
        WatchMap.remove(watchCode);
        WebSocketServer.sendMsg(watchVo.getClientId(), msg);
    }

    private void printLastLines(String clientId, Path file, int lines) {
        List<String> linesList;
        try {
            linesList = Files.readAllLines(file);
        } catch (IOException e) {
            e.printStackTrace();
            String msg = "WatchMsg:读取文件时发生异常";
            WebSocketServer.sendMsg(clientId, msg);
            return;
        }
        int start = Math.max(linesList.size() - lines, 0);
        for (int i = start; i < linesList.size(); i++) {
            JSONObject jo = new JSONObject();
            jo.put("lineNo", i);
            jo.put("lineMsg", linesList.get(i));
            String msg = "WatchData:" + jo.toJSONString();
            WebSocketServer.sendMsg(clientId, msg);
        }
    }
}
